﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
using System.Xml;
using TIBCO.Rendezvous;
using Message = System.ServiceModel.Channels.Message;

namespace Tibco.ServiceModel
{
    internal sealed class TibcoReplyChannel : TibcoReplyChannelBase
    {
        private TibcoTransportBindingElement m_bindingElement;

        private MessageEncoder m_encoder;

        private CMQueueTransport m_model;

        private CMListener m_consumer;

        private BlockingCollection<MessageReceivedEventArgs> m_queue = new BlockingCollection<MessageReceivedEventArgs>(new ConcurrentQueue<MessageReceivedEventArgs>());

        public TibcoReplyChannel(BindingContext context, CMQueueTransport model, EndpointAddress address) : base(context, address)
        {
            this.m_bindingElement = context.Binding.Elements.Find<TibcoTransportBindingElement>();
            TextMessageEncodingBindingElement maxReceivedMessageSize = context.BindingParameters.Find<TextMessageEncodingBindingElement>();
            maxReceivedMessageSize.ReaderQuotas.MaxStringContentLength = (int)this.m_bindingElement.MaxReceivedMessageSize;
            if (maxReceivedMessageSize != null)
            {
                this.m_encoder = maxReceivedMessageSize.CreateMessageEncoderFactory().Encoder;
            }
            this.m_model = model;
            this.m_consumer = null;
        }

        public override void Close(TimeSpan timeout)
        {
            Debug.Out("TibcoReplyChannel.Close()", "Closing...");

            if (base.State == CommunicationState.Closed || base.State == CommunicationState.Closing)
            {
                return;
            }

            base.OnClosing();

            this.m_consumer?.Destroy();
            this.m_consumer = null;

            base.OnClosed();
        }

        public override bool EndWaitForRequest(IAsyncResult result)
        {
            throw new NotImplementedException();
        }

        public override void Open(TimeSpan timeout)
        {
            Debug.Out("TibcoReplyChannel.Open()", "Openning Channel...");

            if (base.State != CommunicationState.Created && base.State != CommunicationState.Closed)
            {
                throw new InvalidOperationException(string.Format("Cannot open the channel from the {0} state.", base.State));
            }
            base.OnOpening();
            this.m_consumer = new CMListener(Queue.Default, this.m_model, base.LocalAddress.Uri.PathAndQuery.Replace("/", "").Replace("-", ""), null);
            this.m_consumer.MessageReceived += ((object sender, MessageReceivedEventArgs args) =>
            {
                this.m_queue.Add(args);
                //this.m_consumer.ConfirmMessage(args.Message);
            });

            var t = new Task(() =>
            {
                while (true)
                {
                    if (base.State == CommunicationState.Closed || base.State == CommunicationState.Closing)
                        break;

                    Queue.Default.TimedDispatch(1);
                }

                Debug.Out("************** WARNING **************", "dispathcing loop breaked!");

            }, TaskCreationOptions.LongRunning);
            t.Start();

            base.OnOpened();
        }

        public override RequestContext ReceiveRequest(TimeSpan timeout)
        {
            RequestContext context;
            Message uri;

            try
            {
                var basicDeliverEventArg = this.m_queue.Take();
                var data = (string)basicDeliverEventArg.Message.GetField("DATA");

                Debug.Out("TibcoReplyChannel.ReceiveRequest()", data);

                var bytes = System.Text.Encoding.UTF8.GetBytes(data);
                uri = this.m_encoder.ReadMessage(new MemoryStream(bytes), (int)this.m_bindingElement.MaxReceivedMessageSize);
                uri.Headers.To = base.LocalAddress.Uri;
                context = new TibcoRequestContext(this.m_model.BaseTransport, this.m_encoder, uri, basicDeliverEventArg.Message, timeout);
            }
            catch (Exception ex)
            {
                Debug.Out("TibcoReplyChannel.ReceiveRequest() Exception", ex.Message);

                base.OnFaulted();

                this.m_consumer?.Destroy();
                this.m_consumer = null;

                this.Close();
                context = null;
            }
            return context;
        }

        public override bool TryReceiveRequest(TimeSpan timeout, out RequestContext context)
        {
            context = this.ReceiveRequest(timeout);
            return true;
        }

        public override bool WaitForRequest(TimeSpan timeout)
        {
            throw new NotImplementedException();
        }
    }
}